home *** CD-ROM | disk | FTP | other *** search
-
- /* Generated by Interface Builder */
-
- #import "PacManView.h"
- #import <libc.h> // event stuff, misc.
- #import "FruitView.h"
- #import "PreferencesBrain.h"
- #import "Maze.h"
- #import "Monster.h"
- #import "Player.h"
- #import <appkit/graphics.h> // for drawing
- #import <appkit/NXImage.h> // for tiff rendering
- #import <appkit/Application.h> // event stuff, misc.
-
- // decides which screen (1-6) to load for a given level. Allows us to
- // put them in an arbitrary order.
- static int screens[NUMSCREENS] = { // which maze image (1-6) to use
- 1, 1, 1, 1, 2, 2, 2, 2,
- 3, 3, 3, 4, 4, 5, 5, 5,
- 1, 2, 0, 0, 3, 4, 0, 5 };
-
- // fruit values
- static long fruitval[NUMFRUITVALS + 1] = { 0, 100, 200, 300, 300, 500, 700,
- 700, 1000, 1000, 2000, 2000, 3000, 3000, 3000, 3000, 5000 };
-
-
- @implementation PacManView
-
- - initFrame:(const NXRect *)frm // designated initializer for a view
- {
- [super initFrame:frm];
- begin = 128;
-
- // initialize game variables
- dotEat = NO;
-
- // where the maze is located within our view.
- mazePos.x = 13; mazePos.y = 12;
- myorigin.x = 0; myorigin.y = 0;
- // a convenient rect; this way I don't have to keep creating a bunch of
- // rects of dimensions GHOST_SIZE by GHOST_SIZE; I re-use this one...
- NXSetRect(&eraseRect, 0, 0, GHOST_SIZE, GHOST_SIZE);
- NXSetRect(&textEraseRect, 0, 0, 3 * GHOST_SIZE, 2 * GHOST_SIZE);
- // make sure power dots get drawn
- erasePwr = YES;
-
- return self;
- }
-
- // Called by appDidInit to load up images, etc. that we need.
- - loadPix
- {
- int i;
- const int *gh;
-
- [super loadPix];
-
- [readyText setStringValue:""]; // clear out the TextField.
- [fruitText setStringValue:""]; // clear out the TextField.
- [self addSubview:readyText];
- [self addSubview:fruitText];
- fruitPointCount = 0;
-
- // get textfields for displaying ghost point values
- ghostText[0] = ghost1text;
- ghostText[1] = ghost2text;
- ghostText[2] = ghost3text;
- ghostText[3] = ghost4text;
-
- // build the ghost objects
- gh = [maze ghosts];
- for (i=0; i<=3; i++) {
- ghost[i] = [[Monster alloc]
- initGhost:i player:player maze:maze at:gh[i*2] :gh[i*2+1]];
- [ghostText[i] setStringValue:""]; // clear out the TextFields.
- // It's impossible to get a color that makes this text stand out in
- // all situations. Turns out that whit is the best, since that's the
- // maze color. Trouble is, the "1" in "1600" disappears into the
- // maze wall. I suppose I could use tiffs that have white text
- // ringed with black, but I prefer the TextFields myself. Simpler.
- //[ghostText[i] setTextColor:NX_COLORRED]; // make them all red.
- [self addSubview:ghostText[i]];
- ghostPointCount[i] = 0;
- }
-
- // get the images
- fruit = [NXImage findImageNamed:"Fruit.tiff"];
- gameOver = [NXImage findImageNamed:"GameOver.tiff"];
- [gameOver getSize:&gameOverSize];
-
- return self;
- }
-
- - ghost:(int)i // return ghost #i
- {
- return ghost[i];
- }
-
-
- // State machine...called by the timed entry. This state machine basically
- // controls all the aspects of the game; depending upon it's state, different
- // things can/will happen. The best way to figure this one out is to sit
- // down and draw a diagram show how the states transition from one to the
- // next. Note that there are several counters, etc. that function as smaller
- // independent state machines that operate within the context of this larger
- // state machine. (Dots blinking, monster states, etc. all run independently
- // from main state machine, although the main machine occasionally interrupts
- // things in the sub-machines.) I've not yet had time to fully document the
- // logic involved in this state machine...if I ever have the time, I may do so,
- // but I don't know that anyone would care if I did anyway. Minor changes in
- // here could render the entire game non-functional if you aren't careful!
- // UNLESS YOU KNOW WHAT YOU'RE DOING, DON'T MESS WITH THIS CODE! Take time
- // to understand it fully before playing with it!
- // This is the most ridiculously long method you'll ever see, but there's
- // no really efficient way to break it up. (Each section deals with what
- // happens in a specific state, and breaking into smaller methods isn't worth
- // while, since things are repeated...and it's silly to proliferate subroutines
- // that only get called once and from one place.)
- - autoUpdate:sender
- { // ALL animation is controlled from here!!!
- register int i, pts, lcnt;
- int x, y;
- BOOL flag = NO;
- BOOL updateFlag = NO;
- float gx, gy;
-
- // keep track of how many time we've been called; we can use it do
- // decide when to do certain things...
- if ([NXApp isHidden]) return self; // don't suck cycles if we're hidden
- cycles++;
- if (((![preferences speed]) || ([preferences speed] == 2))
- && (!demoMode)) { // slow speed, so ignore 75% of entries
- if (cycles & 0x03) return self;
- }
-
- if (!(cycles & 0x0f)) { // power dots always blink no matter what.
- [maze blinkPowerDot]; // happens every 16 cycles.
- erasePwr = YES;
- }
-
- if (fruitPointCount) {
- if (!--fruitPointCount) {
- [fruitText setStringValue:""]; // clear out the TextField.
- fruitPointCount = WIPETEXT;
- } }
-
- for (i=0; i<4; i++) {
- if (ghostPointCount[i]) {
- if (!--ghostPointCount[i]) {
- [ghostText[i] setStringValue:""]; // clear out the TextField.
- ghostPointCount[i] = WIPETEXT;
- } } }
-
- if (state == GAMEOVER) {
- // when demowait counter hits WAITFORDEMO, we start up demo mode.
- // it basically restarts the game--but with "demoMode" turned on.
- if (demoWait++ == WAITFORDEMO) {
- state = NORMALSTATE;
- [self restartGame];
- demoMode = YES;
- [controller unpause];
- [[self window] setTitle:"PacMan Demo"];
- [controller zeroScore];
- } }
-
- // in this state, the "Get Ready!" sign has been put up; we stall for a]
- // while and then we take it away and start up the next level.
- if (state == READY) {
- for (i=0; i<=3; i++) {
- [ghost[i] move:self];
- }
- if (!(--begin)) {
- state = NORMALSTATE;
- [readyText setStringValue:""];
- [controller nextLevel:self];
- [player resetPlayer];
- updateFlag = YES;
- } }
-
- // identical to the above, but we re-start on the same level without
- // re-loading the maze...if we did, all the dots would be restored.
- if (state == DIEREADY) {
- for (i=0; i<=3; i++) {
- [ghost[i] move:self];
- }
- if (!(--begin)) {
- state = NORMALSTATE;
- [readyText setStringValue:""];
- [self startScreen];
- [player resetPlayer];
- updateFlag = YES;
- } }
-
- // make the maze blink on and off at the end of the level.
- if (state == BLINK_LEVEL) {
- if (begin--) {
- if (begin < 32) {
- if (!(cycles & 0x03)) { // make the maze blink
- [maze visible:(![maze isVisible])];
- updateFlag = YES;
- } }
- } else {
- state = READY;
- [maze playerPosition:&x :&y];
- [readyText moveTo:(x - 2 * GHOST_SIZE + mazePos.x)
- :(y + mazePos.y)];
- [readyText setStringValue:"Get Ready!"];
- begin = 64;
- }
- }
-
- // stall. when player dies, the player object needs time to get through
- // it's whole sequence.
- if (state == DYING_PAC) {
- if (!(--begin)) { // stall
- if (![player newPlayer]) { // no pacs left == game over.
- state = GAMEOVER;
- [controller gameOver];
- updateFlag = YES;
- } else { // do ready, next pac...
- state = DIEREADY;
- [maze playerPosition:&x :&y];
- [readyText moveTo:(x - 2 * GHOST_SIZE + mazePos.x)
- :(y + mazePos.y)];
- [readyText setStringValue:"Get Ready!"];
- begin = 64;
- } } }
-
- // this state is the meat of the game. move the player and monsters
- // and deal with player/ghost collisions and eating dots and fruit.
- if (state == NORMALSTATE) {
- // move all the ghosts and the pac
- if (!paused) {
- if (demoMode) lcnt = 2;
- else {
- lcnt = [preferences speed] / 2 + 1; // allow hyper speeds:
- // since 0 <= speed <= 3, we'll move one or two frames per update
- }
- while (lcnt) {
- // figure out how to move player
- [player move:self];
-
- // put up/take away the fruit
- if ((numFruits < 2)||demoMode) { // only two fruits per level
- if (fruitCount++ == timeToFruit) { // time for new fruit ?
- fruitOn = DRAW; numFruits++;
- }
- if (fruitCount == ERASEFRUIT) {
- // it's been there a while... so remove it
- timeToFruit = 200 + 16 * (random() & 0x0f);
- fruitOn = ERASE;
- fruitCount = 0;
- }
- }
-
- // handle eating dots
- if ([maze eatDotAt:[player xpos] :[player ypos]]) {
- dotEat = YES;
- } else dotEat = NO;
- if ([maze powerDotAt:[player xpos] :[player ypos]]) {
- flag = YES;
- dotEat = YES;
- }
- if (![maze dots]) {
- state = BLINK_LEVEL;
- begin = 64;
- }
-
- [maze playerPosition:&x :&y]; // get position of fruit
- // see if player ate the fruit
- if (fruitOn == YES) { // fruit's there...
- if ((abs([player ypos] - y) < GHOST_SIZE / 2) &&
- (abs([player xpos] - x) < GHOST_SIZE / 2)) { // ate it!
- fruitOn = ERASE;
- // add value of fruit to the score
- pts = (([controller level] < NUMFRUITVALS)
- ? fruitval[[controller level]]
- : fruitval[NUMFRUITVALS]);
- [controller addToScore:pts];
- // tell the player how many points he/she got
- ftx = x + TEXTOFFSET + mazePos.x;
- fty = y + mazePos.y;
- [fruitText moveTo:ftx :fty];
- if (pts) [fruitText setIntValue:pts];
- // now, align coords to the maze so erase
- // does it's job right (otherwise, we end up SOVERing
- // maze parts twice, and it looks _ugly_!
- ftx -= mazePos.x; fty -= mazePos.y;
- // chop off lower four bits (floor to mult. of 16)
- ftx /= 16; ftx = (ftx << 4) + mazePos.x;
- fty /= 16; fty = (fty << 4) + mazePos.y;
- fruitPointCount = 48;
- } }
-
- // check for player/ghost collision: (and deal with it)
- if ([maze dots]) {
- for (i=0; i<=3; i++) {
- // tell ghost of power dot
- if (flag) [ghost[i] powerDot:YES];
- [ghost[i] at:&gx :&gy];
- if ((abs(gx - [player xpos]) < GHOST_SIZE / 2) &&
- (abs(gy - [player ypos]) < GHOST_SIZE / 2)) {
- // collision! decide who dies...
- switch ([ghost[i] munch]) {
- case YES : { // player got ghost
- // (-munch already added any bonus.)
- gtx[i] = gx + TEXTOFFSET + mazePos.x;
- gty[i] = gy + mazePos.y;
- [ghostText[i] moveTo:gtx[i] :gty[i]];
- [ghostText[i]
- setIntValue:[controller ateGhost]];
- // now, align to maze as above
- gtx[i] -= mazePos.x; gtx[i] /= 16;
- gty[i] -= mazePos.y; gty[i] /= 16;
- gtx[i] = (gtx[i] << 4) + mazePos.x;
- gty[i] = (gty[i] << 4) + mazePos.y;
- ghostPointCount[i] = 48;
- break;
- }
- case NO : { // ghost got player, so die...
- state = DYING_PAC;
- begin = 48; // stall
- [player pacDie];
- fruitOn = NO;
- updateFlag = YES;
- break;
- }
- case HARMLESS :
- default : { // eyes do nothing.
- break;
- } } } } }
-
- // figure out where ghosts will go next
- for (i=0; i<=3; i++) {
- [ghost[i] move:self];
- }
- if (lcnt > 1) { // make movement take effect w/o render
- for (i=0; i<=3; i++) {
- [ghost[i] moveOneFrame];
- }
- [player moveOneFrame];
- }
- lcnt--;
- } } }
- // draw all the changes, if applicable.
- if (updateFlag) [self update];
- if ((state != READY) && (state != DIEREADY)) [self updateSelf:&bounds :1];
- return self;
- }
-
- // This renders the whole screen, much like updateSelf:: below, but since
- // it always redraws the _entire_ screen, it is unnaceptably inefficient for
- // handling individual animation frames.
- - drawSelf:(NXRect *)rects :(int)rectCount // redraws the screen.
- { // right now, it's stupid and always redraws the whole view.
- register int i, f;
- int x, y;
- NXPoint pos;
- NXRect from, bezel, mazeRect;
-
- if ([self window] == nil)
- return self; // exit if no window to draw in
-
- [self lockFocus];
-
- // draw bezel
- NXSetRect(&bezel, bounds.origin.x + 5, bounds.origin.y + 5,
- bounds.size.width - 10, bounds.size.height - 10);
- NXDrawGrayBezel(&bezel, &bounds);
- NXFrameRectWithWidth(&bounds, 5);
- bezel.size.width -= 6;
- bezel.size.height -= 6;
- bezel.origin.x += 3;
- bezel.origin.y += 3;
- [self drawBackground:&bezel]; // put background inside the bezel
- NXSetRect(&mazeRect, 0, 0,
- GHOST_SIZE * BLOCK_WIDTH, GHOST_SIZE * BLOCK_HEIGHT);
- pos.x = 13; pos.y = 12;
- [maze render:&mazeRect at:&mazePos]; // draw the maze
- if ((fruitOn && (fruitOn != ERASE)) && (state != BLINK_LEVEL)) {
- // render the fruit if necessary
- // first, get coordinates of where to draw the fruit
- [maze playerPosition:&x :&y];
- pos.x = x + mazePos.x;
- pos.y = y + mazePos.y;
- // decide which fruit to draw
- f = fruits[(([controller level] > FRUIT_LEVELS) ?
- FRUIT_LEVELS : [controller level])];
- // draw the fruit
- NXSetRect(&from,
- (f % FRUIT_PER_ROW) * FRUIT_SIZE,
- (f / FRUIT_PER_ROW) * FRUIT_SIZE,
- FRUIT_SIZE, FRUIT_SIZE);
- [fruit composite:NX_SOVER fromRect:&from toPoint:&pos];
- fruitOn = YES;
- }
- if (fruitOn == ERASE) fruitOn = NO;
- if ((state != BLINK_LEVEL) || ((state == BLINK_LEVEL) && (begin > 31)))
- [player renderAt:mazePos.x :mazePos.y move:NO]; // draw the pacman
- if ((state != DYING_PAC) && (state != BLINK_LEVEL) &&
- (state != GAMEOVER)) { // draw the ghosts
- for (i=0; i<=3; i++) {
- [ghost[i] renderAt:mazePos.x :mazePos.y move:NO];
- } }
-
- [self unlockFocus];
- if (fruitPointCount) [fruitText display];
- for (i=0; i<4; i++) if (ghostPointCount[i]) [ghostText[i] display];
- [self lockFocus];
- if (state == GAMEOVER) {
- pos.x = (NX_WIDTH(&bounds) - gameOverSize.width) / 2;
- pos.y = (NX_HEIGHT(&bounds) - gameOverSize.height) / 2;
- [gameOver composite:NX_SOVER toPoint:&pos];
- }
- [self unlockFocus];
- NXPing();
- return self;
- }
-
- // This is the main drawing here. It works like drawSelf, but only
- // _changes_ stuff; it doesn't re-draw the whole view each time. This
- // has been done to speed things up. We erase the old, and then redraw
- // all the spots we erased after calculating where things have moved to.
- - updateSelf:(NXRect *)rects :(int)rectCount // redraws the screen.
- { // it redraws only what has changed since last redraw.
- register int f, i;
- int x, y;
- const int *pd; // pointer to array of power dot coords
- NXRect from;
- NXPoint pos;
-
- if ([self window] == nil) return self; // exit if no window to draw in
-
- [self lockFocus];
- pd = [maze powerDot]; // get power dot coords
- // erase ghosts and power dots by drawing background image on top.
- for (i=0; i<=3; i++) {
- if ((ghostPointCount[i] == WIPETEXT) && (state != GAME_OVER)) {
- ghostPointCount[i] = 0;
- ZAPRECT(textEraseRect, gtx[i], gty[i]);
- NX_X(&textEraseRect) -= mazePos.x;
- NX_Y(&textEraseRect) -= mazePos.y;
- [maze render:&textEraseRect at:&mazePos]; // render maze there
- }
- if (state != GAME_OVER) {
- [ghost[i] lastAt:&NX_X(&eraseRect) :&NX_Y(&eraseRect)];
- NX_X(&eraseRect) += mazePos.x;
- NX_Y(&eraseRect) += mazePos.y;
- [self drawBackground:&eraseRect];
- } if (erasePwr) { // erase power dot every time it blinks.
- NX_X(&eraseRect) = pd[i * 2 ] * GHOST_SIZE + mazePos.x;
- NX_Y(&eraseRect) = pd[i * 2 + 1] * GHOST_SIZE + mazePos.y;
- [self drawBackground:&eraseRect];
- } }
-
- // erase fruit text
- if ((fruitPointCount == WIPETEXT) && (state != GAME_OVER)) {
- fruitPointCount = 0;
- ZAPRECT(textEraseRect, ftx, fty);
- NX_X(&textEraseRect) -= mazePos.x;
- NX_Y(&textEraseRect) -= mazePos.y;
- [maze render:&textEraseRect at:&mazePos]; // render maze there
- }
-
- // erase fruit if needed
- if ((fruitOn) && (state != GAME_OVER)) {
- // inefficient -- we always re draw it when it's on...
- [maze playerPosition:&x :&y];
- NX_X(&eraseRect) = x + mazePos.x;
- NX_Y(&eraseRect) = y + mazePos.y;
- [self drawBackground:&eraseRect];
- }
-
- // erase the player's PacMan
- if (state != GAME_OVER) {
- [player lastAt:&NX_X(&eraseRect) :&NX_Y(&eraseRect)];
- NX_X(&eraseRect) += mazePos.x;
- NX_Y(&eraseRect) += mazePos.y;
- [self drawBackground:&eraseRect];
- [player lastAt:&NX_X(&eraseRect) :&NX_Y(&eraseRect)];
- [maze render:&eraseRect at:&mazePos]; // render maze
- } if (dotEat) { // makes sure that the eaten dot was erased.
- // if I don't do this, when a dot is eaten, if the player quickly
- // reverses direction, the dot will remain--looking half eaten, even
- // though we internally register it as eaten.
- [maze lastDot:&x :&y];
- NX_X(&eraseRect) = x + mazePos.x;
- NX_Y(&eraseRect) = y + mazePos.y;
- [self drawBackground:&eraseRect]; // draw background over it
- NX_X(&eraseRect) = x;
- NX_Y(&eraseRect) = y;
- [maze render:&eraseRect at:&mazePos]; // render maze there
- dotEat = NO;
- }
-
- // render maze underneath the fruit if necessary
- if ((fruitOn) && (state != GAME_OVER)) {
- // inefficient -- we always re draw it when it's on...
- [maze playerPosition:&x :&y];
- NX_X(&eraseRect) = x;
- NX_Y(&eraseRect) = y;
- [maze render:&eraseRect at:&mazePos]; // render maze there
- }
-
- // draw the maze over where the ghosts were.
- for (i=0; i<=3; i++) {
- [ghost[i] lastAt:&NX_X(&eraseRect) :&NX_Y(&eraseRect)];
- [maze render:&eraseRect at:&mazePos];
- if (erasePwr) { // will put power dot on with blinks.
- NX_X(&eraseRect) = pd[i * 2 ] * GHOST_SIZE;
- NX_Y(&eraseRect) = pd[i * 2 + 1] * GHOST_SIZE;
- [maze render:&eraseRect at:&mazePos];
- } }
-
- // render the fruit if necessary
- if (fruitOn) {
- if ((fruitOn != ERASE) && (state != BLINK_LEVEL)) {
- // inefficient -- we always re draw it when it's on...
- // first, get coordinates of where to draw the fruit
- [maze playerPosition:&x :&y];
- pos.x = x + mazePos.x;
- pos.y = y + mazePos.y;
- // decide which fruit to draw
- f = fruits[(([controller level] > FRUIT_LEVELS) ?
- FRUIT_LEVELS : [controller level])];
- // draw the fruit
- NXSetRect(&from,
- (f % FRUIT_PER_ROW) * FRUIT_SIZE,
- (f / FRUIT_PER_ROW) * FRUIT_SIZE,
- FRUIT_SIZE, FRUIT_SIZE);
- [fruit composite:NX_SOVER fromRect:&from toPoint:&pos];
- fruitOn = YES;
- } else { fruitOn = NO; }
- }
-
- // put the player's Pac on the screen if in a state where it's visible.
- if ((state != BLINK_LEVEL) || ((state == BLINK_LEVEL) && (begin > 31)))
- [player renderAt:mazePos.x :mazePos.y
- move:((!paused) && (state == NORMALSTATE))];
-
- // put ghosts on screen if we're in a state where they are visible.
- if ((state != DYING_PAC) && (state != BLINK_LEVEL) &&
- (state != GAMEOVER)) {
- for (i=0; i<=3; i++) {
- [ghost[i] renderAt:mazePos.x :mazePos.y move:
- ((!paused) && ((state == READY) || (state == NORMALSTATE)))];
- } }
-
- /*if (state == GAMEOVER) {
- pos.x = (NX_WIDTH(&bounds) - gameOverSize.width) / 2;
- pos.y = (NX_HEIGHT(&bounds) - gameOverSize.height) / 2;
- [gameOver composite:NX_SOVER toPoint:&pos];
- }*/
- // housekeeping for the graphics:
- [self unlockFocus];
- if (fruitOn == ERASE) fruitOn = NO;
- if (fruitPointCount) [fruitText display];
- for (i=0; i<4; i++) if (ghostPointCount[i]) [ghostText[i] display];
- [[self window] flushWindow];
- NXPing();
- erasePwr = NO; // we've listened to this flag, so turn it off now.
- return self;
- }
-
- // Handle player movement. We respond to the arrow keys. If we don't
- // recognize the key, we pass it on. This method just shunts the key
- // off to the player object, which is what really deals with it.
- - keyDown:(NXEvent *)myevent
- {
- if (!myevent) return self; // if no event when coalescing, go away
- if (!(myevent->flags&(NX_CONTROLMASK|NX_ALTERNATEMASK|NX_COMMANDMASK)) ) {
- if (myevent->data.key.keyCode == 0x16) { // Up Arrow
- [player newDirection:PAC_UP];
- [controller unpause];
- return self;
- } else if (myevent->data.key.keyCode == 0x0f) { // Down Arrow
- [player newDirection:PAC_DOWN];
- [controller unpause];
- return self;
- } else if (myevent->data.key.keyCode == 0x09) { // Left Arrow
- [player newDirection:PAC_LEFT];
- [controller unpause];
- return self;
- } else if (myevent->data.key.keyCode == 0x10) { // Right Arrow
- [player newDirection:PAC_RIGHT];
- [controller unpause];
- return self;
- } else [super keyDown:myevent];
- } else [super keyDown:myevent];
- [self keyDown:[NXApp peekAndGetNextEvent:NX_KEYDOWN]]; // coalesce keydowns
- // this is done recursively...primitive, but easy to implement :-)
- return self;
- }
-
- // set up the screen at the start of a new level. Loads in the maze.
- - setUpScreen
- {
- [super setUpScreen];
- [maze makeMaze:screens[(([controller level] >= NUMSCREENS) ?
- (NUMSCREENS - 1) : [controller level]) - 1]];
- [self startScreen];
- timeToFruit = 200 + 16 * (random() & 0x0f);
- fruitOn = NO;
- fruitCount = 0;
- numFruits = 0;
-
- return self;
- }
-
- // This let's us put the ghosts back where they are at the start of the level.
- // This is separate from above because the above also resets the dots, and if
- // the player dies, we only want to reset the ghosts, not the dots, too.
- - startScreen
- {
- const int *gh; int i;
-
- gh = [maze ghosts]; // pointer to array of ghost coordinates
- for (i=0; i<=3; i++) { // re-initialize each ghost
- [ghost[i] initGhost:i player:player maze:maze at:gh[i*2] :gh[i*2+1]];
- }
- // don't let fruit come out too quickly
- if (timeToFruit - fruitCount < 60)
- timeToFruit = 200 + 16 * (random() & 0x0f);
-
- return self;
- }
-
- - restartGame
- {
- int x, y;
-
- // go to READY state to start game; but want DIEREADY so we don't advance
- // the level; the controller, by virtue of calling this method, has already
- // advance the level.
- state = DIEREADY;
- begin = 96;
-
- // put up the "Get Ready!" sign.
- [maze playerPosition:&x :&y];
- [readyText moveTo:(x - 2 * GHOST_SIZE + mazePos.x) :(y + mazePos.y)];
- [readyText setStringValue:"Get Ready!"];
-
- // make sure that all artifacts of demo mode are gone
- demoWait = 0;
- if (demoMode) {
- demoMode = NO;
- [[self window] setTitle:"PacMan"];
- }
-
- [self setUpScreen]; // load maze, etc.
- [controller zeroScore]; // clear the score
- [self getPreferences]; // make sure we're up to date
- [player newPlayer]; // get a new pac to play with
- // (above always sets up the pac, but in demoMode, the gameBrain hasn't
- // given up 3 pacs, so we end up taking a negative # of pacs, which means
- // demo mode will end as soon as the pac dies.)
- [self update]; // redraw screen
- return self;
- }
-
-
- // These three methods load the three hardcoded background images.
- // To work, the images must be in the same directory as the executable.
- - back1:sender
- {
- char *tempStr;
-
- tempStr = malloc(256); sprintf(tempStr, "%sGradation.eps", appPath);
- [self setBackgroundFile:tempStr andRemember:YES];
- free(tempStr); [self display];
- return self;
- }
-
- - back2:sender
- {
- char *tempStr;
-
- tempStr = malloc(256); sprintf(tempStr, "%slush.tiff", appPath);
- [self setBackgroundFile:tempStr andRemember:YES];
- free(tempStr); [self display];
- return self;
- }
-
- - back3:sender
- {
- char *tempStr;
-
- tempStr = malloc(256); sprintf(tempStr, "%sSunset.tiff", appPath);
- [self setBackgroundFile:tempStr andRemember:YES];
- free(tempStr); [self display];
- return self;
- }
-
-
- @end
-